﻿#include "export_config.hh"
#include "../../../box/service_box.hh"
#include "../../../detail/box_config_impl.hh"
#include "../lua_helper.hh"

#define IMPLEMENT_LUA_METHOD(name)                                             \
  int kratos::lua::LuaConfig::lua_##name(lua_State *l) {                       \
    LuaUtil::NilPusher pusher(l);                                              \
    auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");     \
    if (!config || !config->box_) {                                            \
      return pusher.return_value();                                            \
    }                                                                          \
    return pusher.return_value(config->box_->get_config().name());             \
  }

#define REGISTER_LUA_METHOD(name)                                              \
  LuaUtil::register_function(L_, "_config_" #name, &LuaConfig::lua_##name);

kratos::lua::LuaConfig::LuaConfig(kratos::service::ServiceBox *box,
                                  lua_State *L, LuaServiceImpl *service) {
  box_ = box;
  L_ = L;
  service_ = service;
}

kratos::lua::LuaConfig::~LuaConfig() { listener_name_list_.clear(); }

auto kratos::lua::LuaConfig::do_register() -> bool {
  set_class(L_, this, "_box_config");
  lua_reg_key_ = LuaUtil::new_registry_table(L_);
  box_->get_config().add_reload_listener(
      "lua_config", std::bind(&LuaConfig::reload_listener, this,
                              std::placeholders::_1, std::placeholders::_2));

  REGISTER_LUA_METHOD(get_listener_list);
  REGISTER_LUA_METHOD(get_service_finder_type);
  REGISTER_LUA_METHOD(get_service_finder_hosts);
  REGISTER_LUA_METHOD(get_service_finder_connect_timeout);
  REGISTER_LUA_METHOD(get_necessary_service);
  REGISTER_LUA_METHOD(get_connect_other_box_timeout);
  REGISTER_LUA_METHOD(get_box_channel_recv_buffer_len);
  REGISTER_LUA_METHOD(get_box_name);
  REGISTER_LUA_METHOD(get_logger_config_line);
  REGISTER_LUA_METHOD(get_service_dir);
  REGISTER_LUA_METHOD(get_preload_service);
  REGISTER_LUA_METHOD(is_open_coroutine);
  REGISTER_LUA_METHOD(get_remote_service_repo_version_api);
  REGISTER_LUA_METHOD(get_remote_service_repo_dir);
  REGISTER_LUA_METHOD(get_remote_service_repo_latest_version_api);
  REGISTER_LUA_METHOD(is_open_remote_update);
  REGISTER_LUA_METHOD(get_remote_repo_check_interval);
  REGISTER_LUA_METHOD(is_start_as_daemon);
  REGISTER_LUA_METHOD(get_http_max_call_timeout);
  REGISTER_LUA_METHOD(is_open_rpc_stat);
  REGISTER_LUA_METHOD(has_attribute);
  REGISTER_LUA_METHOD(get_array);
  REGISTER_LUA_METHOD(get_table);
  REGISTER_LUA_METHOD(get_string);
  REGISTER_LUA_METHOD(get_number);
  REGISTER_LUA_METHOD(get_integer);
  REGISTER_LUA_METHOD(add_reload_listener);
  REGISTER_LUA_METHOD(remove_reload_listener);

  box_->write_log_line(klogger::Logger::VERBOSE,
                       "[lua][config]Config module installed");

  return true;
}

auto kratos::lua::LuaConfig::update(std::time_t ms) -> void {
  if (reloaded_) {
    for (const auto &name : listener_name_list_) {
      service_->thread_call(lua_reg_key_, name);
    }
  }
  reloaded_ = false;
}

auto kratos::lua::LuaConfig::do_cleanup() -> void {
  for (const auto &name : listener_name_list_) {
    box_->get_config().remove_reload_listener(name);
  }
  listener_name_list_.clear();
  if (lua_reg_key_ != LUA_NOREF) {
    LuaUtil::remove_registry_key(L_, lua_reg_key_);
  }
}

IMPLEMENT_LUA_METHOD(get_listener_list)
IMPLEMENT_LUA_METHOD(get_service_finder_type)
IMPLEMENT_LUA_METHOD(get_service_finder_hosts)
IMPLEMENT_LUA_METHOD(get_service_finder_connect_timeout)
IMPLEMENT_LUA_METHOD(get_necessary_service)
IMPLEMENT_LUA_METHOD(get_connect_other_box_timeout)
IMPLEMENT_LUA_METHOD(get_box_channel_recv_buffer_len)
IMPLEMENT_LUA_METHOD(get_box_name)
IMPLEMENT_LUA_METHOD(get_logger_config_line)
IMPLEMENT_LUA_METHOD(get_service_dir)
IMPLEMENT_LUA_METHOD(get_preload_service)
IMPLEMENT_LUA_METHOD(is_open_coroutine)
IMPLEMENT_LUA_METHOD(get_remote_service_repo_version_api)
IMPLEMENT_LUA_METHOD(get_remote_service_repo_dir)
IMPLEMENT_LUA_METHOD(get_remote_service_repo_latest_version_api)
IMPLEMENT_LUA_METHOD(is_open_remote_update)
IMPLEMENT_LUA_METHOD(get_remote_repo_check_interval)
IMPLEMENT_LUA_METHOD(is_start_as_daemon)
IMPLEMENT_LUA_METHOD(get_http_max_call_timeout)
IMPLEMENT_LUA_METHOD(is_open_rpc_stat)

int kratos::lua::LuaConfig::lua_has_attribute(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  const auto *s = LuaUtil::get<const char*>(l, -1);
  return pusher.return_value(config->box_->get_config().has_attribute(s));
}

int kratos::lua::LuaConfig::lua_get_array(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *s = lua_tostring(l, -1);
  try {
    auto array_ret = config->box_->get_config().get_array<std::string>(s);
    return pusher.return_value(array_ret);
  } catch (std::exception &e) {
    config->box_->write_log_line(klogger::Logger::EXCEPTION,
                                 std::string("[lua][config]") + e.what());
  }
  return pusher.return_value();
}

int kratos::lua::LuaConfig::lua_get_table(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *s = lua_tostring(l, -1);
  try {
    auto map_ret = config->box_->get_config().get_table<std::string>(s);
    return pusher.return_value(map_ret);
  } catch (std::exception &e) {
    config->box_->write_log_line(klogger::Logger::EXCEPTION,
                                 std::string("[lua][config]") + e.what());
  }
  return pusher.return_value();
}

int kratos::lua::LuaConfig::lua_get_string(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *s = lua_tostring(l, -1);
  try {
    auto str = config->box_->get_config().get_string(s);
    return pusher.return_value(str);
  } catch (std::exception &e) {
    config->box_->write_log_line(klogger::Logger::EXCEPTION,
                                 std::string("[lua][config]") + e.what());
  }
  return pusher.return_value();
}

int kratos::lua::LuaConfig::lua_get_number(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *s = lua_tostring(l, -1);
  try {
    auto number = config->box_->get_config().get_number<lua_Number>(s);
    return pusher.return_value(number);
  } catch (std::exception &e) {
    config->box_->write_log_line(klogger::Logger::EXCEPTION,
                                 std::string("[lua][config]") + e.what());
  }
  return pusher.return_value();
}

int kratos::lua::LuaConfig::lua_get_integer(lua_State *l) {
  LuaUtil::NilPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *s = lua_tostring(l, -1);
  try {
    auto integer = config->box_->get_config().get_number<std::int64_t>(s);
    return pusher.return_value((std::int64_t)integer);
  } catch (std::exception &e) {
    config->box_->write_log_line(klogger::Logger::EXCEPTION,
                                 std::string("[lua][config]") + e.what());
  }
  return pusher.return_value();
}

int kratos::lua::LuaConfig::lua_add_reload_listener(lua_State *l) {
  LuaUtil::BoolPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isfunction(l, -1)) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -2)) {
    return pusher.return_value();
  }
  auto *name = lua_tostring(l, -2);
  auto ret = lua_rawgeti(l, LUA_REGISTRYINDEX, config->lua_reg_key_);
  if (!ret) {
    // 建立失败
    return false;
  }
  LuaUtil::lua_push(l, name);
  lua_pushvalue(l, -3);
  lua_settable(l, -3);
  lua_pop(l, 1);
  config->listener_name_list_.push_back(name);
  return pusher.return_value(true);
}

int kratos::lua::LuaConfig::lua_remove_reload_listener(lua_State *l) {
  LuaUtil::BoolPusher pusher(l);
  auto *config = LuaExportClass::get_class<LuaConfig>(l, "_box_config");
  if (!config || !config->box_) {
    return pusher.return_value();
  }
  if (!lua_isstring(l, -1)) {
    return pusher.return_value();
  }
  auto *name = lua_tostring(l, -1);
  lua_rawgeti(l, LUA_REGISTRYINDEX, config->lua_reg_key_);
  LuaUtil::lua_push(l, name);
  lua_pushnil(l);
  lua_settable(l, -3);
  lua_pop(l, 1);
  config->listener_name_list_.remove(name);
  return pusher.return_value(true);
}

auto kratos::lua::LuaConfig::reload_listener(const std::string &,
                                             const kratos::config::BoxConfig &)
    -> void {
  reloaded_ = true;
}
